package fem2;

import java.util.ArrayList;
import java.util.Collections;

import math2.ConstantFunctionRnToR;
import math2.FunctionBasisOnRnToR;
import math2.FunctionRnToR;

import org.junit.Test;

import fem2.analysis.Analysis;
import fem2.enu.EchoLevelType;
import fem2.enu.SortingCriteria;
import fem2.level_set.Crack;
import fem2.observer.TimeStepObserver;
import fem2.observer.XfemManager;
import fem2.pre_and_post.PatchMeshGenerator;
import fem2.strategies.NonAdaptiveNewtonRaphsonStrategyForXfem;
import fem2.strategies.Strategy;

public class XfemTest2 extends XfemTest1 {

	public XfemTest2() {
		projectDir = "/home/hbui/kratos_janosch";
		projectName = "mesh5x5.gid";
		meshFileName = "simple_mesh.msh";
	}

	@Override
	public Mesh createMesh() {
		MeshGenerator mg = new PatchMeshGenerator(6, 6, 0, 0, 0.2, 0.2);
		// MeshGenerator mg = new PatchMeshGenerator(3, 4, 0, 0, 0.5, 1.0 / 3);
		// MeshGenerator mg = new PatchMeshGenerator(4, 4, 0.0, 0.0, 1.0 / 3,
		// 1.0 / 3);
		// MeshGenerator mg = new PatchMeshGenerator(12, 12, 0.0, 0.0, 1.0 / 11,
		// 1.0 / 11);
		return mg.getMesh(2);
	}

	// @Override
	// public Model createConditions(Model m) {
	// Mesh mesh = m.getMesh();
	//
	// Constraint cx = new Constraint(false, true);
	// Constraint cy = new Constraint(true, false);
	//
	// ArrayList<Node> bottomNodes = MeshUtilities.seekNodesOnSurface(mesh, 0,
	// 1, 0);
	// mesh.addConstraint(cy, bottomNodes);
	// MeshUtilities.seekNode(mesh, 0.0, 0.0).addConstraint(cx);
	//
	// ArrayList<Node> topNodes = MeshUtilities.seekNodesOnSurface(mesh, 0, 1,
	// -1);
	// for (Node n : topNodes) {
	// n.setSortingCriteria(SortingCriteria.SORT_ON_X_COORDINATE);
	// }
	// Collections.sort(topNodes);
	// for (Node n : topNodes) {
	// n.setSortingCriteria(SortingCriteria.SORT_ON_ID);
	// }
	//
	// FunctionRnToR f = new ConstantFunctionRnToR(2.0);
	// for (int i = 0; i < topNodes.size() - 1; i++) {
	// Node n1 = topNodes.get(i);
	// Node n2 = topNodes.get(i + 1);
	// Edge e = mesh.addEdge(new Edge(n1, n2));
	// e.setNumberOfFields(2);
	// Load l = new DistributedLoad(e, null, f);
	// m.addLoad(l);
	// }
	//
	// return m;
	// }

	@Override
	public Model createConditions(Model m) {
		Mesh mesh = m.getMesh();

		Constraint cx = new Constraint(false, true);
		Constraint cy = new Constraint(true, false);

		ArrayList<Node> rightNodes = MeshUtilities.seekNodesOnSurface(mesh, 1, 0, -1);
		mesh.addConstraint(cx, rightNodes);
		MeshUtilities.seekNode(mesh, 1.0, 1.0).addConstraint(cy);

		ArrayList<Node> bottomNodes = MeshUtilities.seekNodesOnSurface(mesh, 0, 1, 0);
		for (Node n : bottomNodes) {
			n.setSortingCriteria(SortingCriteria.SORT_ON_X_COORDINATE);
		}
		Collections.sort(bottomNodes);
		for (Node n : bottomNodes) {
			n.setSortingCriteria(SortingCriteria.SORT_ON_ID);
		}

		ArrayList<Node> topNodes = MeshUtilities.seekNodesOnSurface(mesh, 0, 1, -1);
		for (Node n : topNodes) {
			n.setSortingCriteria(SortingCriteria.SORT_ON_X_COORDINATE);
		}
		Collections.sort(topNodes);
		for (Node n : topNodes) {
			n.setSortingCriteria(SortingCriteria.SORT_ON_ID);
		}

		ArrayList<Node> leftNodes = MeshUtilities.seekNodesOnSurface(mesh, 0, 1, 0);
		for (Node n : leftNodes) {
			n.setSortingCriteria(SortingCriteria.SORT_ON_Y_COORDINATE);
		}
		Collections.sort(leftNodes);
		for (Node n : leftNodes) {
			n.setSortingCriteria(SortingCriteria.SORT_ON_ID);
		}

		FunctionRnToR f1 = new ConstantFunctionRnToR(2.0);
		for (int i = 0; i < topNodes.size() - 1; i++) {
			Node n1 = topNodes.get(i);
			Node n2 = topNodes.get(i + 1);
			Edge e = mesh.addEdge(new Edge(n1, n2));
			e.setNumberOfFields(2);
			Load l = new DistributedLoad(e, null, f1);
			m.addLoad(l);
		}
		FunctionRnToR f2 = new ConstantFunctionRnToR(-2.0);
		for (int i = 0; i < bottomNodes.size() - 1; i++) {
			Node n1 = bottomNodes.get(i);
			Node n2 = bottomNodes.get(i + 1);
			Edge e = mesh.addEdge(new Edge(n1, n2));
			e.setNumberOfFields(2);
			Load l = new DistributedLoad(e, null, f2);
			m.addLoad(l);
		}
		// for (int i = 0; i < leftNodes.size() - 1; i++) {
		// Node n1 = leftNodes.get(i);
		// Node n2 = leftNodes.get(i + 1);
		// Edge e = mesh.addEdge(new Edge(n1, n2));
		// e.setNumberOfFields(2);
		// Load l = new DistributedLoad(e, f2, null);
		// m.addLoad(l);
		// }

		return m;
	}

	@Test
	public void testFindingBoundary() {
		Model m = createModel();

		Topology to = new Topology(m);
		m.getMesh().setTopology(to);

		for (Node n : to.getBoundaryNodes()) {
			Debugger.watch(n.getId());
		}
	}

	@Test
	public void testJdomain() {
		Model m = createModel();

		Topology to = new Topology(m);
		m.getMesh().setTopology(to);

		double[] tip_start = new double[] { 0.0, 0.5 };
		double[] tip_delta = new double[] { 0.5, 0.0 };

		Crack c = new Crack(m, tip_start, tip_delta);
		c.setJIntegralRadius(1.0);
		XfemManager x = new XfemManager(m);
		x.addCrack(c);

		x.fireStarted();
		x.fireTimeStepStarted();

		m.Initialize();

		// c.computeSIFs(m);

	}

	@Override
	public void run(int nt) {
		Model m = createModel();

		Topology to = new Topology(m);
		m.getMesh().setTopology(to);

		Analysis an = new Analysis(m);
		an.setStrategy(new NonAdaptiveNewtonRaphsonStrategyForXfem(m, nt));
		Strategy s = an.getStrategy();
		// s.setSolverType(SolverType.BAITSCH_SOLVER_GENERIC);
		s.setEchoLevel(EchoLevelType.OUTPUT, EchoLevelType.AUX1);

		double[] tip_start = new double[] { 0.0, 0.5 };
		double[] tip_delta = new double[] { 0.5, 0.0 };

		Crack c = new Crack(m, tip_start, tip_delta);
		c.setJIntegralRadius(1.0);
		c.setPropagationStep(0.1);

		XfemManager x = new XfemManager(m);

		x.addCrack(c);

		s.addObserver(x);

		addFundamentalObservers(m, s, new TimeStepObserver(s));

		an.run();

		postProcess(m);

		Node n0 = m.getMesh().getNode(0);
		Debugger.watch(n0.getUHatLocal());

		Face f = m.getMesh().getFace(0);
		double[] uh = f.getUHatLocal();
		FunctionBasisOnRnToR basis = f.getBasis();
		double[] u0 = new double[2];
		for (int i = 0; i < basis.getDimension(); i++) {
			u0[0] += basis.getBasisFunction(i).valueAt(-1, -1) * uh[2 * i];
			u0[1] += basis.getBasisFunction(i).valueAt(-1, -1) * uh[2 * i + 1];
		}
		Debugger.watch(u0);

		Debugger.warn("Analysis completed");
	}

	public static void main(String[] args) {
		XfemTest2 demo = new XfemTest2();

		// demo.run();

		demo.run(4);
	}
}
